﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Shared;

namespace Kea
{
    public static class Enumerable
    {
        public static IEnumerable<TEnumerable> Except<TEnumerable, T1>(this IEnumerable<TEnumerable> enumerable, DataPlanJoinableBase<T1> dataPlan, Func<TEnumerable, object> keyProvider)
            where T1 : TableReaderWriter<T1>, new()
        {

            using (var ep = dataPlan.GetEnumerableProvider<T1>(q => q))
            {
                foreach (TEnumerable te in enumerable)
                {
                    if (ep.GetEnumerable(keyProvider(te)).GetEnumerator().MoveNext()) yield return te;
                }
            }

            //using (IEnumerator<TEnumerable> emt = enumerable.GetEnumerator())
            //{
            //    if (!emt.MoveNext()) yield break;
            //    using (TRWManager<T1> man = new TRWManager<T1>(dataPlan))
            //    {
            //        man.LoadIndexDetails();
            //        Key ks = new Key();
            //        do
            //        {
            //            object key = keyProvider(emt.Current);
            //            if (!man.ReadToStartOfRange(ks.GetKey(key))) yield return emt.Current;
            //        } while (emt.MoveNext());
            //    }
            //}
        }

        public static DataPlanJoined<TEnumerable, T1> Join<TEnumerable, T1>(this IEnumerable<TEnumerable> enumerable, DataPlanJoinableBase<T1> dataPlan, Func<TEnumerable, object> keyProvider)
            where T1 : TableReaderWriter<T1>, new()
        {
            return new DataPlanJoined<TEnumerable, T1>() { TheEnumerable = enumerable, ReaderKeyProvider = keyProvider, JoinProvider = dataPlan.GetJoinable() };
        }

        public static DataPlanJoined<TEnumerable, T1> Join<TEnumerable, T1>(this IEnumerable<TEnumerable> enumerable, DataPlanJoinableBase<T1> dataPlan)
            where T1 : TableReaderWriter<T1>, new()
        {
            return Join(enumerable, dataPlan, e => e);
        }

        public static DataPlanJoined<TEnumerable, TResult> Lookup<TEnumerable, T1, TResult>(this IEnumerable<TEnumerable> enumerable, DataPlanLookupable<T1, TResult> dataPlan, Func<TEnumerable, object> keyProvider)
            where T1 : TableReaderWriter<T1>, new()
        {
            return new DataPlanJoined<TEnumerable, TResult>() { TheEnumerable = enumerable, ReaderKeyProvider = keyProvider, JoinProvider = dataPlan.GetJoinableLookup() };
        }

        public static DataPlanJoined<TEnumerable, TResult> Lookup<TEnumerable, T1, TResult>(this IEnumerable<TEnumerable> enumerable, DataPlanLookupable<T1, TResult> dataPlan)
            where T1 : TableReaderWriter<T1>, new()
        {
            return Lookup(enumerable, dataPlan, e => e);
        }

        //public static DPJoinedBase<TEnumerable, TResult> Lookup<
        //    TEnumerable,
        //    T1,
        //    TResult>(
        //    this IEnumerable<TEnumerable> enumerable,
        //    DPLookupable<T1, TResult> dataPlan,
        //    Func<TEnumerable, object> keyProvider)
        //    where T1 : TableReaderWriter<T1>, new()
        //{
        //    return new DPJoined<
        //        TEnumerable,
        //        TResult>(
        //        null
        //        , null
        //        , enumerable
        //        , keyProvider
        //        , dataPlan.GetJoinableLookup<TResult>());
        //}

        public static WriteableNavigator<TEnumerable, T1> WriteTo<TEnumerable, T1>(this IEnumerable<TEnumerable> enumerable, DataPlanJoinableBase<T1> dataThing, Func<TEnumerable, object> keyProvider)
            where T1 : TableReaderWriter<T1>, new()
        {
            return new WriteableNavigator<TEnumerable, T1>(enumerable, dataThing, keyProvider);
        }


        public static int WriteTo<TEnumerable, T1>(this IEnumerable<TEnumerable> enumerable, DataPlanJoinableBase<T1> dataThing, Func<TEnumerable, object> keyProvider, Action<TEnumerable, T1> update)
            where T1 : TableReaderWriter<T1>, new()
        {
            using (var wn = new WriteableNavigator<TEnumerable, T1>(enumerable, dataThing, keyProvider))
            {
                int k = 0;
                while (wn.Read())
                {
                    update(wn.Item0, wn.Item);
                    wn.Save();
                    k++;
                }
                return k;
            }
        }
    }


    public class WriteableNavigator<TEnumerable, T1> : IDisposable where T1 : TableReaderWriter<T1>, new()
    {
        private readonly IEnumerator<TEnumerable> _enumerator;
        private readonly TRWManagerWithSaveAndNavigate<T1> _man;
        private readonly Func<TEnumerable, object> _keyProvider;

        internal WriteableNavigator(IEnumerable<TEnumerable> original, DataPlanBaseTRW<T1> dataThing, Func<TEnumerable, object> keyProvider)
        {
            _keyProvider = keyProvider;
            _man = new TRWManagerWithSaveAndNavigate<T1>(dataThing);
            _man.LoadIndexDetails();
            try
            {
                _enumerator = original.GetEnumerator();
            }
            catch
            {
                ((IDisposable)_man).Dispose();
                throw;
            }
        }
        private bool _readFromEnumator = true;
        private object[] _keys;
        private Key _keyGetter = new Key();
        public bool Read()
        {
            while (true)
            {
                if (_readFromEnumator)
                {
                    if (!_enumerator.MoveNext()) return false;
                    Item0 = _enumerator.Current;

                    _keys = _keyGetter.GetKey(_keyProvider(Item0));

                    if (!_man.ReadToStartOfRange(_keys)) continue;

                    _readFromEnumator = false;
                    return true;
                }

                if (_man.ReadNextInRange(_keys)) return true;
                _readFromEnumator = true;
            }
        }

        public TEnumerable Item0 { get; private set; }
        public T1 Item { get { return _man.Item; } }
        public void Save() { _man.Save(); }

        public void Dispose()
        {
            ((IDisposable)_man).Dispose();
            _enumerator.Dispose();
        }

    }


}
